/**
 * SPDX-FileCopyrightText: 2021-2024 Infineon Technologies AG
 * SPDX-License-Identifier: MIT
 *
 * \author Infineon Technologies AG
 *
 * \file pal_i2c.c
 *
 * \brief   This file implements the platform abstraction layer(pal) APIs for I2C.
 *
 * \ingroup  grPAL
 *
 * @{
 */

#include "pal_i2c.h"

#include <DAVE.h>

#define PAL_I2C_MASTER_MAX_BITRATE (400U)
/// @cond hidden

_STATIC_H volatile uint32_t g_entry_count = 0;
_STATIC_H const pal_i2c_t *gp_pal_i2c_current_ctx;

// lint --e{715} suppress "This is implemented for overall completion of API"
_STATIC_H pal_status_t pal_i2c_acquire(const void *p_i2c_context) {
    if (0 == g_entry_count) {
        g_entry_count++;
        if (1 == g_entry_count) {
            return PAL_STATUS_SUCCESS;
        }
    }
    return PAL_STATUS_FAILURE;
}

// lint --e{715} suppress "The unused p_i2c_context variable is kept for future enhancements"
_STATIC_H void pal_i2c_release(const void *p_i2c_context) {
    g_entry_count = 0;
}
/// @endcond

void invoke_upper_layer_callback(const pal_i2c_t *p_pal_i2c_ctx, optiga_lib_status_t event) {
    upper_layer_callback_t upper_layer_handler;
    // lint --e{611} suppress "void* function pointer is type casted to upper_layer_callback_t type"
    upper_layer_handler = (upper_layer_callback_t)p_pal_i2c_ctx->upper_layer_event_handler;

    upper_layer_handler(p_pal_i2c_ctx->p_upper_layer_ctx, event);

    // Release I2C Bus
    pal_i2c_release(p_pal_i2c_ctx->p_upper_layer_ctx);
}

/// @cond hidden
void i2c_master_end_of_transmit_callback(void) {
    invoke_upper_layer_callback(gp_pal_i2c_current_ctx, PAL_I2C_EVENT_SUCCESS);
}

void i2c_master_end_of_receive_callback(void) {
    invoke_upper_layer_callback(gp_pal_i2c_current_ctx, PAL_I2C_EVENT_SUCCESS);
}

void i2c_master_error_detected_callback(void) {
    I2C_MASTER_t *p_i2c_master;

    p_i2c_master = gp_pal_i2c_current_ctx->p_i2c_hw_config;
    if (0 != I2C_MASTER_IsTxBusy(p_i2c_master)) {
        // lint --e{534} suppress "Error handling is not required so return value is not checked"
        I2C_MASTER_AbortTransmit(p_i2c_master);
        while (I2C_MASTER_IsTxBusy(p_i2c_master)) {
        }
    }

    if (0 != I2C_MASTER_IsRxBusy(p_i2c_master)) {
        // lint --e{534} suppress "Error handling is not required so return value is not checked"
        I2C_MASTER_AbortReceive(p_i2c_master);
        while (I2C_MASTER_IsRxBusy(p_i2c_master)) {
        }
    }

    invoke_upper_layer_callback(gp_pal_i2c_current_ctx, PAL_I2C_EVENT_ERROR);
}

void i2c_master_nack_received_callback(void) {
    i2c_master_error_detected_callback();
}

void i2c_master_arbitration_lost_callback(void) {
    i2c_master_error_detected_callback();
}
/// @endcond

pal_status_t pal_i2c_init(const pal_i2c_t *p_i2c_context) {
    return PAL_STATUS_SUCCESS;
}

pal_status_t pal_i2c_deinit(const pal_i2c_t *p_i2c_context) {
    return PAL_STATUS_SUCCESS;
}

pal_status_t pal_i2c_write(const pal_i2c_t *p_i2c_context, uint8_t *p_data, uint16_t length) {
    pal_status_t status = PAL_STATUS_FAILURE;

    // Acquire the I2C bus before read/write
    if (PAL_STATUS_SUCCESS == pal_i2c_acquire(p_i2c_context)) {
        gp_pal_i2c_current_ctx = p_i2c_context;

        // Invoke the low level i2c master driver API to write to the bus
        if (I2C_MASTER_STATUS_SUCCESS
            != I2C_MASTER_Transmit(
                p_i2c_context->p_i2c_hw_config,
                (bool)TRUE,
                (p_i2c_context->slave_address << 1),
                p_data,
                length,
                (bool)TRUE
            )) {
            // If I2C Master fails to invoke the write operation, invoke upper layer event handler with error.

            // lint --e{611} suppress "void* function pointer is type casted to upper_layer_callback_t type"
            ((upper_layer_callback_t)(p_i2c_context->upper_layer_event_handler)
            )(p_i2c_context->p_upper_layer_ctx, PAL_I2C_EVENT_ERROR);

            // Release I2C Bus
            pal_i2c_release((void *)p_i2c_context);
        } else {
            status = PAL_STATUS_SUCCESS;
        }
    } else {
        status = PAL_STATUS_I2C_BUSY;
        // lint --e{611} suppress "void* function pointer is type casted to upper_layer_callback_t type"
        ((upper_layer_callback_t)(p_i2c_context->upper_layer_event_handler)
        )(p_i2c_context->p_upper_layer_ctx, PAL_I2C_EVENT_BUSY);
    }
    return status;
}

pal_status_t pal_i2c_read(const pal_i2c_t *p_i2c_context, uint8_t *p_data, uint16_t length) {
    pal_status_t status = PAL_STATUS_FAILURE;

    // Acquire the I2C bus before read/write
    if (PAL_STATUS_SUCCESS == pal_i2c_acquire(p_i2c_context)) {
        gp_pal_i2c_current_ctx = p_i2c_context;

        // Invoke the low level i2c master driver API to read from the bus
        if (I2C_MASTER_STATUS_SUCCESS
            != I2C_MASTER_Receive(
                p_i2c_context->p_i2c_hw_config,
                (bool)TRUE,
                (p_i2c_context->slave_address << 1),
                p_data,
                length,
                (bool)TRUE,
                (bool)TRUE
            )) {
            // If I2C Master fails to invoke the read operation, invoke upper layer event handler with error.

            // lint --e{611} suppress "void* function pointer is type casted to upper_layer_callback_t type"
            ((upper_layer_callback_t)(p_i2c_context->upper_layer_event_handler)
            )(p_i2c_context->p_upper_layer_ctx, PAL_I2C_EVENT_ERROR);

            // Release I2C Bus
            pal_i2c_release((void *)p_i2c_context);
        } else {
            status = PAL_STATUS_SUCCESS;
        }
    } else {
        status = PAL_STATUS_I2C_BUSY;
        // lint --e{611} suppress "void* function pointer is type casted to upper_layer_callback_t type"
        ((upper_layer_callback_t)(p_i2c_context->upper_layer_event_handler)
        )(p_i2c_context->p_upper_layer_ctx, PAL_I2C_EVENT_BUSY);
    }
    return status;
}

pal_status_t pal_i2c_set_bitrate(const pal_i2c_t *p_i2c_context, uint16_t bitrate) {
    pal_status_t return_status = PAL_STATUS_FAILURE;
    optiga_lib_status_t event = PAL_I2C_EVENT_ERROR;

    // Acquire the I2C bus before setting the bitrate
    if (PAL_STATUS_SUCCESS == pal_i2c_acquire(p_i2c_context)) {
        // If the user provided bitrate is greater than the I2C master hardware maximum supported value,
        // set the I2C master to its maximum supported value.
        if (bitrate > PAL_I2C_MASTER_MAX_BITRATE) {
            bitrate = PAL_I2C_MASTER_MAX_BITRATE;
        }
        if (XMC_I2C_CH_STATUS_OK
            != XMC_I2C_CH_SetBaudrate(
                ((I2C_MASTER_t *)p_i2c_context->p_i2c_hw_config)->channel,
                bitrate * 1000
            )) {
            return_status = PAL_STATUS_FAILURE;
        } else {
            return_status = PAL_STATUS_SUCCESS;
            event = PAL_I2C_EVENT_SUCCESS;
        }
    } else {
        return_status = PAL_STATUS_I2C_BUSY;
        event = PAL_I2C_EVENT_BUSY;
    }
    if (0 != p_i2c_context->upper_layer_event_handler) {
        // lint --e{611} suppress "void* function pointer is type casted to upper_layer_callback_t type"
        ((callback_handler_t)(p_i2c_context->upper_layer_event_handler)
        )(p_i2c_context->p_upper_layer_ctx, event);
    }
    // Release I2C Bus if its acquired
    if (PAL_STATUS_I2C_BUSY != return_status) {
        pal_i2c_release((void *)p_i2c_context);
    }
    return return_status;
}

/**
 * @}
 */
